package org.shj.utils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

/**
 * @author Shen Huang Jian
 * @date 2020-08-31 9:28
 */
public class DbUtils {

    private static String username;

    private static String password;

    private static String url;

    private static String driverClass;

    private static List<Connection> connections;

    private static volatile boolean isInitialized;

    private static Connection genConnection() throws Exception{
        if(!isInitialized){
            username = PropertyUtils.getString("db.username");
            password = PropertyUtils.getString("db.password");
            url = PropertyUtils.getString("db.url");
            driverClass = PropertyUtils.getString("db.driverClass");
        }
        Class.forName(driverClass);
        return DriverManager.getConnection(url, username, password);
    }


    private static Connection getConnection() throws Exception{
        if(isInitialized){
            if(connections.isEmpty()){
                connections.add(genConnection());
            }
            return connections.remove(0);
        }else{
            connections = new ArrayList<>();
            for(int i = 0; i < 2; i++){
                connections.add(genConnection());
            }
            return connections.remove(0);
        }

    }

    public static <T> List<T> findBySql(String sql, Class<T> clazz) throws Exception{
        Connection connection = getConnection();
        Statement statement = connection.createStatement();
        ResultSet rs = statement.executeQuery(sql);
        List<T> results = new ArrayList<>();

        MapInfo mapInfo = getMapInfo(clazz);
        ResultSetMetaData metaData = rs.getMetaData();

        while(rs.next()){

            T data = clazz.newInstance();
            for(int i = 1 ; i <= metaData.getColumnCount(); i++){
//                System.out.println("metaData.getColumnLabel(): " + metaData.getColumnLabel(i)
//                        + ";  metaData.getColumnName(): " + metaData.getColumnName(i));
                mapInfo.getColumnFieldMap().get(metaData.getColumnLabel(i)).set(data, rs.getObject(i));
            }

            results.add(data);
        }
        closeConnection(connection);
        return results;
    }

    private static <T> MapInfo getMapInfo(Class<T> clazz){
        List<Field> fields = getAllFields(clazz);
        MapInfo mapInfo = new MapInfo();
        Map<String, String> fieldColumnMap = new HashMap<>();
        Map<String, String> columnFieldNameMap = new HashMap<>();
        Map<String, Field> columnFieldMap = new HashMap<>();

        for(Field field : fields){
            field.setAccessible(true);
            String columnLabel = StringUtils.convertFieldNameToColumnName(field.getName());
            fieldColumnMap.put(columnLabel, field.getName());
            columnFieldNameMap.put( field.getName(), columnLabel);
            columnFieldMap.put(columnLabel, field);
        }
        mapInfo.setColumnFieldMap(columnFieldMap);
        mapInfo.setColumnFieldNameMap(columnFieldNameMap);
        mapInfo.setFieldColumnMap(fieldColumnMap);
        return mapInfo;
    }


    private static <T> List<Field> getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<>();
        while(clazz != null){
            fieldList.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }
        return fieldList;
    }

    private static void closeConnection(Connection connection){
        if(connections.size() >= 10){
            try{
                connection.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }else{
            connections.add(connection);
        }
    }
}
